package com.gitee.wsl.transform.converter.sample

import com.gitee.wsl.text.predicate.CharMatcher
import com.gitee.wsl.lang.character.Ascii
import com.gitee.wsl.transform.converter.AbstractConverter


/**
 * Utility class for converting between various ASCII case formats. Behavior is undefined for
 * non-ASCII input.
 *
 * @author Mike Bostock
 * @since 1.0
 */

enum class CaseFormat(private val wordBoundary: CharMatcher, private val wordSeparator: String) {
    /** Hyphenated variable naming convention, e.g., "lower-hyphen".  */
    LOWER_HYPHEN(CharMatcher.char('-'), "-") {
        override fun normalizeWord(word: String): String {
            return Ascii.toLowerCase(word)
        }

        override fun convert(format: CaseFormat, s: String): String {
            if (format === LOWER_UNDERSCORE) {
                return s.replace('-', '_')
            }
            return if (format === UPPER_UNDERSCORE) {
                Ascii.toUpperCase(s.replace('-', '_'))
            } else super.convert(format, s)
        }
    },

    /** C++ variable naming convention, e.g., "lower_underscore".  */
    LOWER_UNDERSCORE(CharMatcher.char('_'), "_") {
        override fun normalizeWord(word: String): String {
            return Ascii.toLowerCase(word)
        }

        override fun convert(format: CaseFormat, s: String): String {
            if (format === LOWER_HYPHEN) {
                return s.replace('_', '-')
            }
            return if (format === UPPER_UNDERSCORE) {
                Ascii.toUpperCase(s)
            } else super.convert(format, s)
        }
    },

    /** Java variable naming convention, e.g., "lowerCamel".  */
    LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") {
        override fun normalizeWord(word: String): String {
            return firstCharOnlyToUpper(word)
        }

        override fun normalizeFirstWord(word: String): String {
            return Ascii.toLowerCase(word)
        }
    },

    /** Java and C++ class naming convention, e.g., "UpperCamel".  */
    UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") {
        override fun normalizeWord(word: String): String {
            return firstCharOnlyToUpper(word)
        }
    },

    /** Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE".  */
    UPPER_UNDERSCORE(CharMatcher.char('_'), "_") {
        override fun normalizeWord(word: String): String {
            return Ascii.toUpperCase(word)
        }

        override fun convert(format: CaseFormat, s: String): String {
            if (format === LOWER_HYPHEN) {
                return Ascii.toLowerCase(s.replace('_', '-'))
            }
            return if (format === LOWER_UNDERSCORE) {
                Ascii.toLowerCase(s)
            } else super.convert(format, s)
        }
    };

    /**
     * Converts the specified `String str` from this format to the specified `format`. A
     * "best effort" approach is taken; if `str` does not conform to the assumed format, then
     * the behavior of this method is undefined but we make a reasonable effort at converting anyway.
     */
    fun to(format: CaseFormat, str: String): String {
        return if (format === this) str else convert(format, str)
    }

    /** Enum values can override for performance reasons.  */
    open fun convert(format: CaseFormat, s: String): String {
        // deal with camel conversion
        var out: StringBuilder? = null
        var i = 0
        var j = -1
        while (wordBoundary.indexIn(s, ++j).also { j = it } != -1) {
            if (i == 0) {
                // include some extra space for separators
                out = StringBuilder(s.length + 4 * format.wordSeparator.length)
                out.append(format.normalizeFirstWord(s.substring(i, j)))
            } else {
                out!!.append(format.normalizeWord(s.substring(i, j)))
            }
            out.append(format.wordSeparator)
            i = j + wordSeparator.length
        }
        return if (i == 0) format.normalizeFirstWord(s) else out!!.append(
            format.normalizeWord(
                s.substring(i)
            )
        ).toString()
    }

    /**
     * Returns a serializable `Converter` that converts strings from this format to `targetFormat`.
     *
     * @since 16.0
     */
    fun converterTo(targetFormat: CaseFormat): AbstractConverter<String, String> {
        return StringConverter(this, targetFormat)
    }

    abstract fun normalizeWord(word: String): String
    open fun normalizeFirstWord(word: String): String {
        return normalizeWord(word)
    }

    companion object {
        private fun firstCharOnlyToUpper(word: String): String {
            return if (word.isEmpty())
                        word
                    else
                        Ascii.toUpperCase(word[0]) + Ascii.toLowerCase(word.substring(1))
        }
    }
}

private class StringConverter(
    private val sourceFormat: CaseFormat,
    private val targetFormat: CaseFormat
) : AbstractConverter<String, String>() {

    override fun doForward(a: String): String {
        return sourceFormat.to(targetFormat, a)
    }

    override fun doBackward(b: String): String {
        return targetFormat.to(sourceFormat, b)
    }

    override fun equals(`object`: Any?): Boolean {
        if (`object` is StringConverter) {
            val that = `object`
            return sourceFormat == that.sourceFormat && targetFormat == that.targetFormat
        }
        return false
    }

    override fun hashCode(): Int {
        return sourceFormat.hashCode() xor targetFormat.hashCode()
    }

    override fun toString(): String {
        return "$sourceFormat.converterTo($targetFormat)"
    }
}


//fun String.changeCaseFormat(from: CaseFormat, to: CaseFormat): String = from.to(to, this)

//fun Converter.caseFormat(type:CaseFormat) = type


